package tps12.axwax.client;

import java.util.ArrayList;
import java.util.Collection;

import com.google.gwt.core.client.GWT;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.RequestBuilder.Method;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.user.client.rpc.AsyncCallback;

/**
 * Encapsulates access to the puzzle servlet.
 * 
 * @author Travis
 *
 */
public class PuzzleClientImpl implements PuzzleClient {
	
	static String SERVLET_NAME = "puzzles";
	
	public final String baseUrl;
	
	final RequestBuilderFactory rbf;
	
	public PuzzleClientImpl(RequestBuilderFactory rbf) {
		if (rbf == null)
			rbf = new GwtRequestBuilderFactory();
		this.rbf = rbf;
		baseUrl = GWT.getModuleBaseURL() + SERVLET_NAME;
	}
	
	public static interface RequestBuilderFactory {
		RequestBuilder getRequestBuilder(Method method, String url);
	}
	
	public static class GwtRequestBuilderFactory implements RequestBuilderFactory {
		@Override
		public RequestBuilder getRequestBuilder(Method method, String url) {
			return new RequestBuilder(method, url);
		}
	}

	void request(Method method, String override, String url, String data,
			final int expected, String accept,
			final AsyncCallback<String> callback) {
		RequestBuilder req = rbf.getRequestBuilder(method, baseUrl + url);
		if (override != null)
			req.setHeader("X-HTTP-Method-Override", override);
		if (data != null)
			req.setRequestData(data);
		
		if (accept != null)
			req.setHeader("Accept", accept);
		req.setCallback(new RequestCallback() {
			@Override public void onError(Request request, Throwable exception) {
				GWT.log("Async call failed", exception);
				callback.onFailure(exception);
			}

			@Override public void onResponseReceived(Request request, Response response) {
				if (response.getStatusCode() == expected)
					callback.onSuccess(response.getText());
				else
					onError(request, new Exception(response.getStatusText()));
			}
		});
		try {
			req.send();
		} catch (RequestException e) {
			callback.onFailure(e);
		}
	}

	/* (non-Javadoc)
	 * @see tps12.axwax.client.PuzzleClient#getPuzzleList(com.google.gwt.user.client.rpc.AsyncCallback)
	 */
	public void getPuzzleList(final AsyncCallback<Collection<PuzzleDescription>> callback) {
		request(RequestBuilder.GET, null, "/", null, Response.SC_OK, "application/json",
				new AsyncCallback<String>() {
					@Override
					public void onFailure(Throwable caught) {
						callback.onFailure(caught);
					}

					@Override
					public void onSuccess(String result) {
						try {
							JSONArray a = JSONParser.parse(result).isArray();
							ArrayList<PuzzleDescription> list = new ArrayList<PuzzleDescription>(a.size()); 
							for (int i = 0; i < a.size(); i++)
							{
								JSONObject o = a.get(i).isObject();
								if (o != null)
									list.add(new PuzzleDescription(
										o.get("title").isString().stringValue(),
										o.get("url").isString().stringValue()));
							}
							callback.onSuccess(list);
						}
						catch (Throwable caught) {
							callback.onFailure(caught);
						}
					}
		});
	}

	/* (non-Javadoc)
	 * @see tps12.axwax.client.PuzzleClient#getPuzzle(int, com.google.gwt.user.client.rpc.AsyncCallback)
	 */
	public void getPuzzle(String url, final AsyncCallback<Puzzle> callback) {
		request(RequestBuilder.GET, null, url, null, Response.SC_OK, "application/json",
				new AsyncCallback<String>() {
					@Override
					public void onFailure(Throwable caught) {
						callback.onFailure(caught);
					}

					@Override
					public void onSuccess(String result) {
						try {
							callback.onSuccess(
								new Puzzle(
									JSONParser.parse(result).isObject()));
						}
						catch (Throwable caught) {
							callback.onFailure(caught);
						}
					}
		});
	}
	
	public void getPuzzleLetters(String url, final AsyncCallback<String> callback) {
		request(RequestBuilder.GET, null, url, null, Response.SC_OK, null, callback);
	}
	
	public void putPuzzleLetters(String url, String letters, final AsyncCallback<Boolean> callback) {
		request(RequestBuilder.POST, "PUT", url, letters, Response.SC_OK, null,
				new AsyncCallback<String>() {
					@Override
					public void onFailure(Throwable caught) {
						callback.onFailure(caught);
					}

					@Override
					public void onSuccess(String result) {
						callback.onSuccess(true);
					}
		});
	}
	
	/* (non-Javadoc)
	 * @see tps12.axwax.client.PuzzleClient#postPuzzle(java.lang.String, com.google.gwt.user.client.rpc.AsyncCallback)
	 */
	public void postPuzzle(String xml, final AsyncCallback<String> callback) {
		request(RequestBuilder.POST, null, "/", xml, Response.SC_CREATED, null, callback);
	}
}
